<!doctype html>
<!--
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<html>
<head>
  <meta charset="utf-8">
  <script src="../../node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js"></script>
  <script src="wct-browser-config.js"></script>
  <script src="../../node_modules/wct-browser-legacy/browser.js"></script>
  <script type="module" src="../../polymer-legacy.js"></script>
  <script type="module" src="./array-selector-elements.js"></script>
<body>

<test-fixture id="singleConfigured">
  <template>
    <array-selector
      items='[{"name": "one"},{"name": "two"},{"name": "three"}]'>
    </array-selector>
  </template>
</test-fixture>

<test-fixture id="multiConfigured">
  <template>
    <array-selector multi
      items='[{"name": "one"},{"name": "two"},{"name": "three"}]'>
    </array-selector>
  </template>
</test-fixture>

<test-fixture id="bind">
  <template>
    <dom-bind>
      <template>
        <observe-el id="observer" single-selected="{{singleSelected}}" multi-selected="{{multiSelected}}"></observe-el>
        <array-selector id="singleBound" items="{{items}}" selected="{{singleSelected}}"></array-selector>
        <array-selector id="multiBound" items="{{items}}" selected="{{multiSelected}}" multi></array-selector>
      </template>
    </dom-bind>
  </template>
</test-fixture>

<script type="module">
import './array-selector-elements.js';
suite('single selection', function() {

  test('single selection', function() {
    var el = fixture('singleConfigured');
    // Nothing selected
    assert.strictEqual(el.selected, null);
    assert.strictEqual(el.selectedItem, null);
    assert.isFalse(el.isSelected(el.items[0]));
    assert.isFalse(el.isSelected(el.items[1]));
    assert.isFalse(el.isSelected(el.items[2]));
    // Select 0
    el.select(el.items[0]);
    assert.strictEqual(el.selected, el.items[0]);
    assert.strictEqual(el.selectedItem, el.items[0]);
    assert.isTrue(el.isSelected(el.items[0]));
    assert.isFalse(el.isSelected(el.items[1]));
    assert.isFalse(el.isSelected(el.items[2]));
    // Re-select 0
    el.select(el.items[0]);
    assert.strictEqual(el.selected, el.items[0]);
    assert.strictEqual(el.selectedItem, el.items[0]);
    assert.isTrue(el.isSelected(el.items[0]));
    assert.isFalse(el.isSelected(el.items[1]));
    assert.isFalse(el.isSelected(el.items[2]));
    // Select 2 (using index-based API)
    el.selectIndex(2);
    assert.strictEqual(el.selected, el.items[2]);
    assert.strictEqual(el.selectedItem, el.items[2]);
    assert.isFalse(el.isIndexSelected(0));
    assert.isFalse(el.isIndexSelected(1));
    assert.isTrue(el.isIndexSelected(2));
    // Toggle 2
    el.toggle = true;
    el.select(el.items[2]);
    assert.strictEqual(el.selected, null);
    assert.strictEqual(el.selectedItem, null);
    assert.isFalse(el.isSelected(el.items[0]));
    assert.isFalse(el.isSelected(el.items[1]));
    assert.isFalse(el.isSelected(el.items[2]));
    // Toggle 2
    el.toggle = true;
    el.select(el.items[2]);
    assert.strictEqual(el.selected, el.items[2]);
    assert.strictEqual(el.selectedItem, el.items[2]);
    assert.isFalse(el.isSelected(el.items[0]));
    assert.isFalse(el.isSelected(el.items[1]));
    assert.isTrue(el.isSelected(el.items[2]));
    // clearSelection
    el.clearSelection();
    assert.equal(el.selectedItem, null);
  });

  test('bound defaults', function() {
    let bind = fixture('bind');
    assert.equal(bind.$.observer.singleSelected, null);
    assert.sameMembers(bind.$.observer.multiSelected, []);
    bind.items = [
      {name: 'one'},
      {name: 'two'},
      {name: 'three'}
    ];
    assert.equal(bind.$.observer.singleSelected, null);
    assert.sameMembers(bind.$.observer.multiSelected, []);
  });

  test('single selection notification', function() {
    let bind = fixture('bind');
    bind.items = [
      {name: 'one'},
      {name: 'two'},
      {name: 'three'}
    ];
    bind.$.observer.singleChanged = sinon.spy();
    bind.$.singleBound.select(bind.items[2]);
    assert.isTrue(bind.$.observer.singleChanged.calledOnce);
    assert.equal(bind.$.observer.singleChanged.firstCall.args[0].path, 'singleSelected');
    assert.equal(bind.$.observer.singleChanged.firstCall.args[0].value, bind.items[2]);
    assert.equal(bind.$.observer.singleSelected, bind.items[2]);
    // clear selection
    bind.$.observer.singleChanged = sinon.spy();
    bind.$.singleBound.clearSelection();
    assert.equal(bind.$.observer.singleSelected, null);
    assert.isTrue(bind.$.observer.singleChanged.calledOnce);
    assert.equal(bind.$.observer.singleChanged.firstCall.args[0].path, 'singleSelected');
    assert.equal(bind.$.observer.singleChanged.firstCall.args[0].value, null);
  });

  test('single selection sub-property change', function() {
    let bind = fixture('bind');
    bind.items = [
      {name: 'one'},
      {name: 'two'},
      {name: 'three'}
    ];
    bind.$.singleBound.select(bind.items[2]);
    bind.$.observer.singleChanged = sinon.spy();
    bind.set(['items', 2, 'name'], 'test');
    assert.isTrue(bind.$.observer.singleChanged.calledOnce);
    assert.equal(bind.$.observer.singleChanged.firstCall.args[0].path, 'singleSelected.name');
    assert.equal(bind.$.observer.singleChanged.firstCall.args[0].value, 'test');
    assert.equal(bind.$.observer.singleSelected.name, 'test');

    bind.$.singleBound.select(bind.items[1]);
    bind.$.observer.singleChanged = sinon.spy();
    bind.set(['items', 1, 'name'], 'test2');
    assert.isTrue(bind.$.observer.singleChanged.calledOnce);
    assert.equal(bind.$.observer.singleChanged.firstCall.args[0].path, 'singleSelected.name');
    assert.equal(bind.$.observer.singleChanged.firstCall.args[0].value, 'test2');
    assert.equal(bind.$.observer.singleSelected.name, 'test2');
  });

  test('single selection sub-property change after unshift', function() {
    let bind = fixture('bind');
    bind.items = [
      {name: 'one'},
      {name: 'two'},
      {name: 'three'}
    ];
    bind.$.singleBound.select(bind.items[2]);
    bind.unshift('items', {name: 'zero'});
    bind.$.observer.singleChanged = sinon.spy();
    bind.set(['items', 3, 'name'], 'test2');
    assert.isTrue(bind.$.observer.singleChanged.calledOnce);
    assert.equal(bind.$.observer.singleChanged.firstCall.args[0].path, 'singleSelected.name');
    assert.equal(bind.$.observer.singleChanged.firstCall.args[0].value, 'test2');
    assert.equal(bind.$.observer.singleSelected.name, 'test2');
  });

  test('single selection removal via splice', function() {
    let bind = fixture('bind');
    bind.items = [
      {name: 'one'},
      {name: 'two'},
      {name: 'three'}
    ];
    bind.$.singleBound.select(bind.items[1]);
    bind.$.observer.singleChanged = sinon.spy();
    bind.splice('items', 1, 2);
    assert.isTrue(bind.$.observer.singleChanged.calledOnce);
    assert.equal(bind.$.observer.singleChanged.firstCall.args[0].path, 'singleSelected');
    assert.equal(bind.$.observer.singleChanged.firstCall.args[0].value, null);
    assert.equal(bind.$.observer.singleSelected, null);
  });

  test('copy array, selection should remain', function() {
    let bind = fixture('bind');
    bind.items = [
      {name: 'one'},
      {name: 'two'},
      {name: 'three'}
    ];
    bind.$.singleBound.select(bind.items[2]);
    assert.equal(bind.$.singleBound.selected, bind.items[2]);
    // set items to copy; all should still be selected
    bind.items = bind.items.slice();
    assert.equal(bind.$.singleBound.selected, bind.items[2]);
  });

  test('change array, selection should go away', function() {
    let bind = fixture('bind');
    bind.items = [
      {name: 'one'},
      {name: 'two'},
      {name: 'three'}
    ];
    bind.$.singleBound.select(bind.items[2]);
    assert.equal(bind.$.singleBound.selected, bind.items[2]);
    // set items to new objects; all should be removed (selection based on identity)
    bind.items = [
      {name: 'one'},
      {name: 'two'},
      {name: 'three'}
    ];
    assert.equal(bind.$.singleBound.selected, null);
  });

  test('null array, selection should go away', function() {
    let bind = fixture('bind');
    bind.items = [
      {name: 'one'},
      {name: 'two'},
      {name: 'three'}
    ];
    bind.$.singleBound.select(bind.items[2]);
    assert.equal(bind.$.singleBound.selected, bind.items[2]);
    // set items to new objects; all should be removed (selection based on identity)
    bind.items = null;
    assert.equal(bind.$.singleBound.selected, null);
  });

});

suite('multi selection', function() {

  test('multi selection', function() {
    var el = fixture('multiConfigured');
    // Nothing selected
    assert.sameMembers(el.selected, []);
    assert.isFalse(el.isSelected(el.items[0]));
    assert.isFalse(el.isSelected(el.items[1]));
    assert.isFalse(el.isSelected(el.items[2]));
    // Select 0
    el.select(el.items[0]);
    assert.sameMembers(el.selected, [el.items[0]]);
    assert.isTrue(el.isSelected(el.items[0]));
    assert.isFalse(el.isSelected(el.items[1]));
    assert.isFalse(el.isSelected(el.items[2]));
    // Re-select 0
    el.select(el.items[0]);
    assert.sameMembers(el.selected, [el.items[0]]);
    assert.isTrue(el.isSelected(el.items[0]));
    assert.isFalse(el.isSelected(el.items[1]));
    assert.isFalse(el.isSelected(el.items[2]));
    // Select 2 (using index-based API)
    el.selectIndex(2);
    assert.sameMembers(el.selected, [el.items[0], el.items[2]]);
    assert.isTrue(el.isIndexSelected(0));
    assert.isFalse(el.isIndexSelected(1));
    assert.isTrue(el.isIndexSelected(2));
    // Toggle 2
    el.toggle = true;
    el.select(el.items[2]);
    assert.sameMembers(el.selected, [el.items[0]]);
    assert.isTrue(el.isSelected(el.items[0]));
    assert.isFalse(el.isSelected(el.items[1]));
    assert.isFalse(el.isSelected(el.items[2]));
    // Toggle 2
    el.toggle = true;
    el.select(el.items[2]);
    assert.sameMembers(el.selected, [el.items[0], el.items[2]]);
    assert.isTrue(el.isSelected(el.items[0]));
    assert.isFalse(el.isSelected(el.items[1]));
    assert.isTrue(el.isSelected(el.items[2]));
    // clear selection
    el.clearSelection();
    assert.sameMembers(el.selected, []);
  });
  test('multi selection notification', function() {
    let bind = fixture('bind');
    bind.items = [
      {name: 'one'},
      {name: 'two'},
      {name: 'three'}
    ];
    // select first
    bind.$.observer.multiChanged = sinon.spy();
    bind.$.multiBound.select(bind.items[2]);
    assert.isTrue(bind.$.observer.multiChanged.calledTwice);
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].path, 'multiSelected.splices');
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].value.indexSplices.length, 1);
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].value.indexSplices[0].addedCount, 1);
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].value.indexSplices[0].removed.length, 0);
    assert.sameMembers(bind.$.observer.multiSelected, [bind.items[2]]);
    assert.equal(bind.$.observer.multiChanged.secondCall.args[0].path, 'multiSelected.length');
    assert.equal(bind.$.observer.multiChanged.secondCall.args[0].value, 1);
    // select second
    bind.$.observer.multiChanged = sinon.spy();
    bind.$.multiBound.select(bind.items[0]);
    assert.isTrue(bind.$.observer.multiChanged.calledTwice);
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].path, 'multiSelected.splices');
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].value.indexSplices.length, 1);
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].value.indexSplices[0].addedCount, 1);
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].value.indexSplices[0].removed.length, 0);
    assert.sameMembers(bind.$.observer.multiSelected, [bind.items[2], bind.items[0]]);
    assert.equal(bind.$.observer.multiChanged.secondCall.args[0].path, 'multiSelected.length');
    assert.equal(bind.$.observer.multiChanged.secondCall.args[0].value, 2);
    // clear selection
    bind.$.observer.multiChanged = sinon.spy();
    bind.$.multiBound.clearSelection();
    assert.sameMembers(bind.$.observer.multiSelected, []);
    assert.isTrue(bind.$.observer.multiChanged.calledOnce);
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].path, 'multiSelected');
    assert.sameMembers(bind.$.observer.multiChanged.firstCall.args[0].value, []);
  });

  test('multi selection sub-property change', function() {
    let bind = fixture('bind');
    bind.items = [
      {name: 'one'},
      {name: 'two'},
      {name: 'three'}
    ];
    bind.$.multiBound.select(bind.items[2]);
    bind.$.multiBound.select(bind.items[0]);
    bind.$.observer.multiChanged = sinon.spy();
    bind.set(['items', 2, 'name'], 'test');
    assert.isTrue(bind.$.observer.multiChanged.calledOnce);
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].path, 'multiSelected.0.name');
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].value, 'test');
    assert.equal(bind.$.observer.multiSelected[0].name, 'test');
    bind.$.observer.multiChanged = sinon.spy();
    bind.set(['items', 0, 'name'], 'test2');
    assert.isTrue(bind.$.observer.multiChanged.calledOnce);
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].path, 'multiSelected.1.name');
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].value, 'test2');
    assert.equal(bind.$.observer.multiSelected[1].name, 'test2');
  });


  test('multi selection sub-property change after unshift', function() {
    let bind = fixture('bind');
    bind.items = [
      {name: 'one'},
      {name: 'two'},
      {name: 'three'}
    ];
    bind.$.multiBound.select(bind.items[2]);
    bind.$.multiBound.select(bind.items[0]);
    bind.unshift('items', {name: 'zero'});
    bind.$.observer.multiChanged = sinon.spy();
    bind.set(['items', 1, 'name'], 'test');
    assert.isTrue(bind.$.observer.multiChanged.calledOnce);
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].path, 'multiSelected.1.name');
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].value, 'test');
    assert.equal(bind.$.observer.multiSelected[1].name, 'test');
  });

  test('multi selection sub-property change after splice (removal)', function() {
    let bind = fixture('bind');
    bind.items = [
      {name: 'one'},
      {name: 'two'},
      {name: 'three'}
    ];
    bind.$.multiBound.select(bind.items[0]);
    bind.$.multiBound.select(bind.items[2]);

    bind.$.observer.multiChanged = sinon.spy();
    bind.splice('items', 0, 1);
    // assert.equal(bind.$.observer.multiChanged.firstCall.args[0].path, 'multiSelected');
    // assert.sameMembers(bind.$.observer.multiChanged.firstCall.args[0].value, [bind.items[1]]);
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].path, 'multiSelected.splices');
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].value.indexSplices.length, 1);
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].value.indexSplices[0].index, 0);
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].value.indexSplices[0].addedCount, 0);
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].value.indexSplices[0].removed.length, 1);

    bind.$.observer.multiChanged = sinon.spy();
    bind.set(['items', 1, 'name'], 'test');
    assert.isTrue(bind.$.observer.multiChanged.calledOnce);
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].path, 'multiSelected.0.name');
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].value, 'test');
    assert.equal(bind.$.observer.multiSelected[0].name, 'test');
  });

  test('multi selection sub-property change after deselect', function() {
    let bind = fixture('bind');
    bind.items = [
      {name: 'one'},
      {name: 'two'},
      {name: 'three'}
    ];
    bind.$.multiBound.select(bind.items[2]);
    bind.$.multiBound.select(bind.items[0]);
    bind.$.observer.multiChanged = sinon.spy();
    bind.set(['items', 0, 'name'], 'test');
    assert.isTrue(bind.$.observer.multiChanged.calledOnce);
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].path, 'multiSelected.1.name');
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].value, 'test');
    assert.equal(bind.$.observer.multiSelected[1].name, 'test');

    bind.$.multiBound.deselect(bind.items[2]);
    bind.$.observer.multiChanged = sinon.spy();
    bind.set(['items', 0, 'name'], 'test4');
    assert.isTrue(bind.$.observer.multiChanged.calledOnce);
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].path, 'multiSelected.0.name');
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].value, 'test4');
    assert.equal(bind.$.observer.multiSelected[0].name, 'test4');
  });

  test('copy array, selection should remain', function() {
    let bind = fixture('bind');
    bind.items = [
      {name: 'one'},
      {name: 'two'},
      {name: 'three'}
    ];
    bind.$.multiBound.select(bind.items[2]);
    bind.$.multiBound.select(bind.items[0]);
    assert.sameMembers(bind.$.multiBound.selected, [bind.items[2], bind.items[0]]);
    // set items to copy; all should still be selected
    bind.items = bind.items.slice();
    assert.sameMembers(bind.$.multiBound.selected, [bind.items[2], bind.items[0]]);
  });

  test('change array, selection should go away', function() {
    let bind = fixture('bind');
    bind.items = [
      {name: 'one'},
      {name: 'two'},
      {name: 'three'}
    ];
    bind.$.multiBound.select(bind.items[2]);
    bind.$.multiBound.select(bind.items[0]);
    assert.sameMembers(bind.$.multiBound.selected, [bind.items[2], bind.items[0]]);
    // set items to new objects; all should be removed (selection based on identity)
    bind.items = [
      {name: 'one'},
      {name: 'two'},
      {name: 'three'}
    ];
    assert.sameMembers(bind.$.multiBound.selected, []);
  });

  test('null array, selection should go away', function() {
    let bind = fixture('bind');
    bind.items = [
      {name: 'one'},
      {name: 'two'},
      {name: 'three'}
    ];
    bind.$.multiBound.select(bind.items[2]);
    bind.$.multiBound.select(bind.items[0]);
    assert.sameMembers(bind.$.multiBound.selected, [bind.items[2], bind.items[0]]);
    // set items to new objects; all should be removed (selection based on identity)
    bind.items = null;
    assert.sameMembers(bind.$.multiBound.selected, []);
  });

  test('reset item in array, selection should go away', function() {
    let bind = fixture('bind');
    bind.items = [
      {name: 'one'},
      {name: 'two'},
      {name: 'three'}
    ];
    bind.$.multiBound.select(bind.items[2]);
    bind.$.multiBound.select(bind.items[0]);
    assert.sameMembers(bind.$.multiBound.selected, [bind.items[2], bind.items[0]]);
    // set items to new objects; all should be removed (selection based on identity)
    bind.set('items.0', {name: 'new'});
    assert.sameMembers(bind.$.multiBound.selected, [bind.items[2]]);
  });

});

suite('corner cases', function() {

  test('switch from single to multi', function() {
    var el = fixture('singleConfigured');
    // Nothing selected
    assert.strictEqual(el.selected, null);
    assert.strictEqual(el.selectedItem, null);
    assert.isFalse(el.isSelected(el.items[0]));
    assert.isFalse(el.isSelected(el.items[1]));
    assert.isFalse(el.isSelected(el.items[2]));
    // Select 0
    el.select(el.items[0]);
    assert.strictEqual(el.selected, el.items[0]);
    assert.strictEqual(el.selectedItem, el.items[0]);
    assert.isTrue(el.isSelected(el.items[0]));
    assert.isFalse(el.isSelected(el.items[1]));
    assert.isFalse(el.isSelected(el.items[2]));
    // switch to multi
    el.multi = true;
    assert.sameMembers(el.selected, []);
    assert.equal(el.selectedItem, null);
  });

  test('switch from multi to single', function() {
    var el = fixture('multiConfigured');
    // Nothing selected
    assert.sameMembers(el.selected, []);
    assert.isFalse(el.isSelected(el.items[0]));
    assert.isFalse(el.isSelected(el.items[1]));
    assert.isFalse(el.isSelected(el.items[2]));
    // Select 0
    el.select(el.items[0]);
    assert.sameMembers(el.selected, [el.items[0]]);
    assert.isTrue(el.isSelected(el.items[0]));
    assert.isFalse(el.isSelected(el.items[1]));
    assert.isFalse(el.isSelected(el.items[2]));
    // switch to single
    el.multi = false;
    assert.equal(el.selected, null);
    assert.equal(el.selectedItem, null);
  });

  test('reset mutated array with gaps resulting in multiple splices', function() {
    var bind = fixture('bind');
    let i = bind.items = [
      {name: '0'}, {name: '1'}, {name: '2'}, {name: '3'},
      {name: '4'}, {name: '5'}, {name: '6'}, {name: '7'}
    ];
    bind.$.multiBound.selectIndex(2);
    bind.$.multiBound.selectIndex(6);
    bind.$.multiBound.selectIndex(1);
    bind.$.multiBound.selectIndex(5);
    bind.$.multiBound.selectIndex(0);
    bind.$.multiBound.selectIndex(4);
    assert.sameMembers(bind.$.multiBound.selected, [i[2], i[6], i[1], i[5], i[0], i[4]]);
    bind.$.observer.multiChanged = sinon.spy();
    bind.set('items.4.name', 'changed1');
    assert.isTrue(bind.$.observer.multiChanged.calledOnce);
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].path, 'multiSelected.5.name');
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].value, 'changed1');

    bind.items = [
      i[0], i[3], i[4], i[7]
    ];
    assert.sameMembers(bind.$.multiBound.selected, [i[0], i[4]]);
    bind.$.observer.multiChanged = sinon.spy();
    bind.set('items.2.name', 'changed2');
    assert.isTrue(bind.$.observer.multiChanged.calledOnce);
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].path, 'multiSelected.1.name');
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].value, 'changed2');
  });

  // Current algorithm does not handle reordering array while maintaining selection state
  test('reset mutated array reordered resulting in multiple splices', function() {
    var bind = fixture('bind');
    let i = bind.items = [
      {name: '0'}, {name: '1'}, {name: '2'}, {name: '3'},
      {name: '4'}, {name: '5'}, {name: '6'}, {name: '7'}
    ];
    bind.$.multiBound.selectIndex(2);
    bind.$.multiBound.selectIndex(6);
    bind.$.multiBound.selectIndex(1);
    bind.$.multiBound.selectIndex(5);
    bind.$.multiBound.selectIndex(0);
    bind.$.multiBound.selectIndex(4);
    assert.sameMembers(bind.$.multiBound.selected, [i[2], i[6], i[1], i[5], i[0], i[4]]);
    bind.$.observer.multiChanged = sinon.spy();
    bind.set('items.4.name', 'changed1');
    assert.isTrue(bind.$.observer.multiChanged.calledOnce);
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].path, 'multiSelected.5.name');
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].value, 'changed1');

    bind.items = [
      i[4], i[5], i[6], i[7], i[0], i[1], i[2], i[3]
    ];
    assert.sameMembers(bind.$.multiBound.selected, [i[2], i[6], i[1], i[5], i[0], i[4]]);
    bind.$.observer.multiChanged = sinon.spy();
    bind.set('items.0.name', 'changed2');
    assert.isTrue(bind.$.observer.multiChanged.calledOnce);
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].path, 'multiSelected.5.name');
    assert.equal(bind.$.observer.multiChanged.firstCall.args[0].value, 'changed2');
  });

});
</script>

</body>
</html>
